/*
 * GraphicalManager.cpp
 *
 *  Created on: Nov 2, 2008
 *      Author: tfannes
 */

#include "LayoutManager.h"
#include <QPainter>
#include "../model/HexGrid.h"
#include "../model/IllegalViewException.h"
#include <math.h>

const QColor LayoutManager::overlayFillColor = QColor(100, 100, 100, 100);
const QColor LayoutManager::overlayPenColor = QColor(255, 0, 0);

LayoutManager::LayoutManager(const QString & tileFile)
{
	if (!initializeTiles(tileFile))
		throw error::IllegalViewException::CreateInvalidFileException(tileFile);
}

LayoutManager::~LayoutManager()
{
}

void LayoutManager::setLayoutData(const QSize & newTileSize)
{
	gridSize = newTileSize - QSize(1, 1);

	int xstep = gridSize.width() / 4;
	int radius = gridSize.width() / 2;
	int ystep = gridSize.height() / 2;
	int width = gridSize.width();
	int height = gridSize.height();

	// hexagon properties
	tileSize = newTileSize;
	tilePolygon.setPoints(6, xstep, 0, width - xstep, 0, width, ystep, width - xstep, height, xstep, height, 0, ystep);

	// corner properties
	cornerRadius = radius / 10;
	cornerSize = QSize(radius / 5 + 1, radius / 5 + 1);

	// side properties
	double sideHeight = radius / 12;
	double cos = 0.5;
	double sin = 0.8660254037844385966;
	int largedx = xstep * cos;
	int largedy = xstep * sin;
	int smalldx = sideHeight * sin;
	int smalldy = sideHeight * cos;

	sidePolygon[0].setPoints(4, 0, largedy, smalldx, smalldy + largedy, smalldx + largedx, smalldy, largedx, 0);
	sidePolygon[1].setPoints(4, 0, 0, xstep, 0, xstep, (int) sideHeight, 0, (int) sideHeight);
	sidePolygon[2].setPoints(4, 0, smalldy, largedx, smalldy + largedy, smalldx + largedx, largedy, smalldx, 0);
	sideSize[0] = QSize(smalldx + largedx + 1, smalldy + largedy + 1);
	sideSize[1] = QSize(xstep + 1, sideHeight + 1);
	sideSize[2] = QSize(smalldx + largedx + 1, smalldy + largedy + 1);
}

bool LayoutManager::initializeTiles(const QString & filename)
{
	QPixmap totalPixmap(filename);
	if (totalPixmap.isNull())
		return false;

	setLayoutData(QSize(totalPixmap.width() / 4, totalPixmap.height() / 10));

	int dx = tileSize.width();
	int dy = tileSize.height();

	arTileResource[board::TileTypeLand][board::TileResourceUndefined] = totalPixmap.copy(0, dy * 9, dx, dy);
	arTileResource[board::TileTypeLand][board::TileResourceBrick] = totalPixmap.copy(0, dy * 8, dx, dy);
	arTileResource[board::TileTypeLand][board::TileResourceGrain] = totalPixmap.copy(0, dy * 7, dx, dy);
	arTileResource[board::TileTypeLand][board::TileResourceOre] = totalPixmap.copy(0, dy * 6, dx, dy);
	arTileResource[board::TileTypeLand][board::TileResourceWood] = totalPixmap.copy(0, dy * 5, dx, dy);
	arTileResource[board::TileTypeLand][board::TileResourceWool] = totalPixmap.copy(0, dy * 4, dx, dy);

	arTileResource[board::TileTypeSea][board::TileResourceUndefined] = totalPixmap.copy(dx, dy * 9, dx, dy);
	arTileResource[board::TileTypeSea][board::TileResourceBrick] = totalPixmap.copy(dx, dy * 8, dx, dy);
	arTileResource[board::TileTypeSea][board::TileResourceGrain] = totalPixmap.copy(dx, dy * 7, dx, dy);
	arTileResource[board::TileTypeSea][board::TileResourceOre] = totalPixmap.copy(dx, dy * 6, dx, dy);
	arTileResource[board::TileTypeSea][board::TileResourceWood] = totalPixmap.copy(dx, dy * 5, dx, dy);
	arTileResource[board::TileTypeSea][board::TileResourceWool] = totalPixmap.copy(dx, dy * 4, dx, dy);
	arTileResource[board::TileTypeSea][board::TileResourceUndefined] = totalPixmap.copy(dx, dy * 3, dx, dy);

	arTileNumber[board::TileNumber2] = totalPixmap.copy(dx * 2, dy * 9, dx, dy);
	arTileNumber[board::TileNumber3] = totalPixmap.copy(dx * 2, dy * 8, dx, dy);
	arTileNumber[board::TileNumber4] = totalPixmap.copy(dx * 2, dy * 7, dx, dy);
	arTileNumber[board::TileNumber5] = totalPixmap.copy(dx * 2, dy * 6, dx, dy);
	arTileNumber[board::TileNumber6] = totalPixmap.copy(dx * 2, dy * 5, dx, dy);
	arTileNumber[board::TileNumber8] = totalPixmap.copy(dx * 2, dy * 4, dx, dy);
	arTileNumber[board::TileNumber9] = totalPixmap.copy(dx * 2, dy * 3, dx, dy);
	arTileNumber[board::TileNumber10] = totalPixmap.copy(dx * 2, dy * 2, dx, dy);
	arTileNumber[board::TileNumber11] = totalPixmap.copy(dx * 2, dy * 1, dx, dy);
	arTileNumber[board::TileNumber12] = totalPixmap.copy(dx * 2, 0, dx, dy);

	arTileOrientation[board::TileOrientation12H] = totalPixmap.copy(dx * 3, dy * 9, dx, dy);
	arTileOrientation[board::TileOrientation2H] = totalPixmap.copy(dx * 3, dy * 8, dx, dy);
	arTileOrientation[board::TileOrientation4H] = totalPixmap.copy(dx * 3, dy * 7, dx, dy);
	arTileOrientation[board::TileOrientation6H] = totalPixmap.copy(dx * 3, dy * 6, dx, dy);
	arTileOrientation[board::TileOrientation8H] = totalPixmap.copy(dx * 3, dy * 5, dx, dy);
	arTileOrientation[board::TileOrientation10H] = totalPixmap.copy(dx * 3, dy * 4, dx, dy);

	tileOverlay = QPixmap(getTileSize());
	tileOverlay.fill(QColor(0,0,0,0));

	QPainter painter(&tileOverlay);
	painter.setBrush(QBrush(overlayFillColor, Qt::SolidPattern));
	painter.setPen(overlayPenColor);
	painter.drawPolygon(tilePolygon, Qt::OddEvenFill);

	return true;
}

// pixmap functions
void LayoutManager::getTilePixmap(const board::TileData & data, QPixmap & pixmapToFill) const
{
	pixmapToFill.fill(QColor(0, 0, 0, 0));
	QPainter painter(&pixmapToFill);

	// draw the tile background
	if (arTileResource.contains(data.type) && arTileResource[data.type].contains(data.resource))
		painter.drawPixmap(0, 0, arTileResource[data.type][data.resource]);

	if (data.type == board::TileTypeLand && arTileNumber.contains(data.number))
		painter.drawPixmap(0, 0, arTileNumber[data.number]);

	if (data.type == board::TileTypeSea && arTileOrientation.contains(data.orientation))
		painter.drawPixmap(0, 0, arTileOrientation[data.orientation]);
}

void LayoutManager::getTileOverlay(QPixmap & pixmapToFill) const
{
	pixmapToFill = tileOverlay;
}

QPixmap LayoutManager::getTilePixmap(const board::TileData & data) const
{
	QPixmap pixmap(getTileSize());
	getTilePixmap(data, pixmap);

	return pixmap;
}

QPixmap LayoutManager::getTileOverlay() const
{
	return tileOverlay;
}

void LayoutManager::getCornerPixmap(const board::CornerBuildingData & data, QPixmap & pixmapToFill) const
{

}

void LayoutManager::getCornerOverlay(QPixmap & pixmapToFill) const
{
	pixmapToFill = cornerOverlay;
}

QPixmap LayoutManager::getCornerPixmap(const board::CornerBuildingData & data) const
{
}

QPixmap LayoutManager::getCornerOverlay() const
{
	return cornerOverlay;
}

void LayoutManager::getSidePixmap(const board::SideBuildingData & data, const structure::HexVector & position, QPixmap & pixmapToFill) const
{

}

void LayoutManager::getSideOverlay(const structure::HexVector & position, QPixmap & pixmapToFill) const
{
	pixmapToFill = sideOverlay[(int)structure::HexGrid::CheckSideType(position)];
}
QPixmap LayoutManager::getSidePixmap(const board::SideBuildingData & data, const structure::HexVector & position) const
{
}
QPixmap LayoutManager::getSideOverlay(const structure::HexVector & position) const
{
	return sideOverlay[(int)structure::HexGrid::CheckSideType(position)];
}

QRect LayoutManager::getElementRect(const structure::HexVector & position, const QSize & windowSize) const
{
	QRect rct;
	getElementRect(position, windowSize, rct);

	return rct;
}

void LayoutManager::getElementRect(const structure::HexVector & position, const QSize & windowSize, QRect & rect) const
{
	QSize size;

	switch((int)structure::HexGrid::CheckLocationType(position))
	{
		case (int)structure::GridLocationHexagon:
			size = getTileSize();
			break;

		case (int)structure::GridLocationCorner:
			size = getCornerSize();
			break;

		case (int)structure::GridLocationSide:
			size = getSideSize(HexGrid::CheckSideType(position));
			break;
	}

	rect.setTopLeft(getElementCenter(position, windowSize) - QPoint(size.width()/2, size.height()/2));
	rect.setSize(size);
}

QPoint LayoutManager::getElementCenter(const structure::HexVector & position, const QSize & windowSize) const
{
	QPoint p;
	getElementCenter(position, windowSize, p);

	return p;
}

void LayoutManager::getElementCenter(const structure::HexVector & position, const QSize & windowSize, QPoint & point) const
{
	float fX = position.getX() / 6.0f - (position.getY() + position.getZ()) / 12.0f;
	float fY = (position.getZ() - position.getY()) / 6.0f;

	point.setX(fX * gridSize.width() / 2 + windowSize.width() / 2);
	point.setY(fY * gridSize.height() / 2 + windowSize.height() / 2);
}



HexVector LayoutManager::getNearestTile(const QPoint & point, const QSize & size) const
{
	float dx = -(point.x() - size.width()/2.0f)/(3.0f*gridSize.width()/2.0f);
	float dy = (point.y() - size.height()/2.0f)/(2.0f*gridSize.height()/2.0f);

	int s = round(dx - dy);
	int t = round(dx + dy);
	int r = -s-t;

	//qDebug("output: %f %f %f", x, y, z);

	return HexVector(r*6, s*6, t*6);
}

HexVector LayoutManager::getNearestCorner(const QPoint & point, const QSize & size) const
{
	HexVector nearestTile = getNearestTile(point, size);

	HexVector nearestCorner;
	float distanceToCorner = 100000;

	for(int i = 0; i < 6; i++)
	{
		HexVector currentCorner = nearestTile + Hexagon::AdjCorners[i];
		QPoint currentCenter = getElementCenter(currentCorner, size);
		float currentDistance = distance2(currentCenter, point);

		if(currentDistance < distanceToCorner)
		{
			distanceToCorner = currentDistance;
			nearestCorner = currentCorner;
		}
	}

	return nearestCorner;
}
HexVector LayoutManager::getNearestSide(const QPoint & point, const QSize & size) const
{
	HexVector nearestTile = getNearestTile(point, size);

		HexVector nearestSide;
		float distanceToSide = 100000;

		for(int i = 0; i < 6; i++)
		{
			HexVector currentSide = nearestTile + Hexagon::AdjSides[i];
			QPoint currentCenter = getElementCenter(currentSide, size);
			float currentDistance = distance2(currentCenter, point);

			if(currentDistance < distanceToSide)
			{
				distanceToSide = currentDistance;
				nearestSide = currentSide;
			}
		}

		return nearestSide;
}

// position check functions
bool LayoutManager::isInTile(const structure::HexVector & position, const QSize & windowSize, const QPoint & point) const
{
	return isInElement(position, windowSize, point);
}
bool LayoutManager::isInCorner(const structure::HexVector & position, const QSize & windowSize, const QPoint & point) const
{
	return isInElement(position, windowSize, point);
}
bool LayoutManager::isInSide(const structure::HexVector & position, const QSize & windowSize, const QPoint & point) const
{
	return isInElement(position, windowSize, point);
}

bool LayoutManager::isInElement(const structure::HexVector & position, const QSize & windowSize, const QPoint & point) const
{
	QRect rct = getElementRect(position, windowSize);

	if(!rct.contains(point))
		return false;

	if(HexGrid::CheckLocationType(position) == GridLocationHexagon)
		return tilePolygon.containsPoint(point - QPoint(windowSize.width()/2, windowSize.height()/2), Qt::OddEvenFill);
	else if(HexGrid::CheckLocationType(position) == GridLocationSide)
		return sidePolygon[HexGrid::CheckSideType(position)].containsPoint(point - QPoint(windowSize.width()/2, windowSize.height()/2), Qt::OddEvenFill);
	else
		return false;
}

float LayoutManager::distance2(const QPoint & point1, const QPoint & point2) const
{
	int dx = point1.x() - point2.x();
	int dy = point1.y() - point2.y();

	return dx*dx + dy*dy;
}
